<!--
/**
 * DicomViewer 组件
 * 
 * DICOM 图像查看器核心组件，提供视口渲染和交互功能
 * 
 * @author yangzhixin
 */
-->

<template>
  <div class="dicom-viewer" :class="{ 'is-loading': isLoading }">
    <!-- 视口容器 -->
    <div
      ref="viewportElement"
      class="viewport-container"
      :style="viewportStyle"
    >
      <!-- 加载指示器 -->
      <div v-if="isLoading" class="loading-overlay">
        <q-spinner-cube color="primary" size="50px" />
        <p class="loading-text">{{ loadingText }}</p>
      </div>

      <!-- 错误提示 -->
      <div v-if="error" class="error-overlay">
        <q-icon name="error" color="negative" size="48px" />
        <p class="error-text">{{ error }}</p>
        <q-btn
          label="重试"
          color="primary"
          @click="handleRetry"
        />
      </div>

      <!-- 信息覆盖层 -->
      <div v-if="showInfo && !isLoading && !error" class="info-overlay">
        <div class="info-top-left">
          <div v-if="patientInfo">
            <div>{{ patientInfo.patientName || '未知患者' }}</div>
            <div>{{ patientInfo.patientID || '' }}</div>
          </div>
        </div>

        <div class="info-top-right">
          <div v-if="studyInfo">
            <div>{{ studyInfo.studyDate || '' }}</div>
            <div>{{ studyInfo.studyDescription || '' }}</div>
          </div>
        </div>

        <div class="info-bottom-left">
          <div v-if="imageInfo">
            <div>WW: {{ imageInfo.windowWidth || 0 }}</div>
            <div>WC: {{ imageInfo.windowCenter || 0 }}</div>
          </div>
        </div>

        <div class="info-bottom-right">
          <div v-if="totalImages > 0">
            <div>{{ currentIndex + 1 }} / {{ totalImages }}</div>
          </div>
        </div>
      </div>
    </div>

    <!-- 导航控件 -->
    <div v-if="showNavigation && totalImages > 1" class="navigation-controls">
      <q-btn
        icon="navigate_before"
        color="primary"
        round
        dense
        :disable="!hasPrevious"
        @click="handlePrevious"
      />
      <span class="navigation-text">
        {{ currentIndex + 1 }} / {{ totalImages }}
      </span>
      <q-btn
        icon="navigate_next"
        color="primary"
        round
        dense
        :disable="!hasNext"
        @click="handleNext"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch, nextTick, toRef } from 'vue';
import { useViewport } from '@/composables/useViewport';
import type { CornerstoneFacade, ViewportType } from 'js-3d-model';

/**
 * 组件属性
 */
interface Props {
  /** CornerstoneFacade 实例 */
  facade: CornerstoneFacade | null;
  /** 视口类型 */
  viewportType?: ViewportType;
  /** 图像ID数组 */
  imageIds?: string[];
  /** 初始图像索引 */
  initialIndex?: number;
  /** 视口宽度 */
  width?: string | number;
  /** 视口高度 */
  height?: string | number;
  /** 是否显示信息覆盖层 */
  showInfo?: boolean;
  /** 是否显示导航控件 */
  showNavigation?: boolean;
  /** 是否启用预加载 */
  enablePreload?: boolean;
  /** 加载提示文本 */
  loadingText?: string;
  /** 患者信息 */
  patientInfo?: {
    patientName?: string;
    patientID?: string;
  };
  /** 检查信息 */
  studyInfo?: {
    studyDate?: string;
    studyDescription?: string;
  };
}

const props = withDefaults(defineProps<Props>(), {
  viewportType: 'STACK' as ViewportType,
  imageIds: () => [],
  initialIndex: 0,
  width: '100%',
  height: '600px',
  showInfo: true,
  showNavigation: true,
  enablePreload: true,
  loadingText: '加载中...',
});

/**
 * 组件事件
 */
interface Emits {
  (e: 'viewport-created', viewportId: string): void;
  (e: 'images-loaded', imageIds: string[]): void;
  (e: 'image-changed', index: number): void;
  (e: 'error', error: Error): void;
}

const emit = defineEmits<Emits>();

// Refs
const viewportElement = ref<HTMLDivElement>();
// 直接使用 toRef 创建一个响应式引用，避免双重包装
const facadeRef = toRef(props, 'facade');

// 使用 useViewport composable
const {
  viewportId,
  isLoading,
  currentImageIndex,
  totalImages,
  windowLevel,
  error,
  hasPrevious,
  hasNext,
  createViewport,
  loadImages,
  previousImage,
  nextImage,
} = useViewport(facadeRef);

// 计算属性
const currentIndex = computed(() => currentImageIndex.value);

const imageInfo = computed(() => {
  if (!windowLevel.value) return null;
  return {
    windowWidth: Math.round(windowLevel.value.windowWidth),
    windowCenter: Math.round(windowLevel.value.windowCenter),
  };
});

const viewportStyle = computed(() => ({
  width: typeof props.width === 'number' ? `${props.width}px` : props.width,
  height: typeof props.height === 'number' ? `${props.height}px` : props.height,
}));

/**
 * 初始化视口
 */
const initializeViewport = async () => {
  if (!props.facade || !viewportElement.value) {
    console.warn('[DicomViewer] Facade 或视口元素未准备好');
    return;
  }

  try {
    const elementId = viewportElement.value.id || `viewport-${Date.now()}`;
    viewportElement.value.id = elementId;

    // 创建视口
    await createViewport(elementId, {
      type: props.viewportType,
      viewportId: `${elementId}-vp`,
    });

    emit('viewport-created', viewportId.value!);

    // 如果有图像ID，加载图像
    if (props.imageIds.length > 0) {
      await handleLoadImages(props.imageIds);
    }
  } catch (err: any) {
    console.error('[DicomViewer] 初始化视口失败:', err);
    emit('error', err);
  }
};

/**
 * 加载图像
 */
const handleLoadImages = async (imageIds: string[]) => {
  try {
    await loadImages({
      imageIds,
      currentIndex: props.initialIndex,
      preload: props.enablePreload,
    });

    emit('images-loaded', imageIds);
  } catch (err: any) {
    console.error('[DicomViewer] 加载图像失败:', err);
    emit('error', err);
  }
};

/**
 * 上一张图像
 */
const handlePrevious = async () => {
  try {
    await previousImage();
    emit('image-changed', currentIndex.value);
  } catch (err: any) {
    console.error('[DicomViewer] 切换到上一张失败:', err);
    emit('error', err);
  }
};

/**
 * 下一张图像
 */
const handleNext = async () => {
  try {
    await nextImage();
    emit('image-changed', currentIndex.value);
  } catch (err: any) {
    console.error('[DicomViewer] 切换到下一张失败:', err);
    emit('error', err);
  }
};

/**
 * 重试
 */
const handleRetry = async () => {
  await initializeViewport();
};

// 监听 imageIds 变化
watch(
  () => props.imageIds,
  async (newImageIds) => {
    if (newImageIds.length > 0 && viewportId.value) {
      await handleLoadImages(newImageIds);
    }
  }
);

// 组件挂载
onMounted(async () => {
  await nextTick();
  if (props.facade) {
    await initializeViewport();
  }
});

// 监听 facade 变化
watch(
  () => props.facade,
  async (newFacade) => {
    if (newFacade && viewportElement.value) {
      await initializeViewport();
    }
  }
);

// 组件卸载
onUnmounted(() => {
  // 清理工作由 useViewport 处理
});

// 暴露方法给父组件
defineExpose({
  viewportId,
  currentIndex,
  totalImages,
  previousImage: handlePrevious,
  nextImage: handleNext,
  refresh: initializeViewport,
});
</script>

<style scoped lang="scss">
.dicom-viewer {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  background-color: #000;
  
  &.is-loading {
    pointer-events: none;
  }
}

.viewport-container {
  position: relative;
  flex: 1;
  overflow: hidden;
  background-color: #000;
}

.loading-overlay,
.error-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.8);
  z-index: 10;
}

.loading-text,
.error-text {
  margin-top: 16px;
  color: #fff;
  font-size: 14px;
}

.error-overlay .q-btn {
  margin-top: 16px;
}

.info-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  pointer-events: none;
  color: #fff;
  font-size: 12px;
  font-family: monospace;
  z-index: 5;

  > div {
    position: absolute;
    padding: 8px;
    background-color: rgba(0, 0, 0, 0.5);
  }

  .info-top-left {
    top: 0;
    left: 0;
  }

  .info-top-right {
    top: 0;
    right: 0;
    text-align: right;
  }

  .info-bottom-left {
    bottom: 0;
    left: 0;
  }

  .info-bottom-right {
    bottom: 0;
    right: 0;
    text-align: right;
  }
}

.navigation-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  padding: 12px;
  background-color: #1e1e1e;
  border-top: 1px solid #333;

  .navigation-text {
    color: #fff;
    font-size: 14px;
    min-width: 80px;
    text-align: center;
  }
}
</style>


